use crate::mqi;
use crate::Result;
use crate::mqheaders::MQHeader;
use crate::{strcpy, set_mqmd_default};

#[derive(Debug)]
pub struct MQMessage {
    mqmd: MQMD,
    data: Vec<u8>,
    headers: Vec<MQHeader>,
}

impl From<&str> for MQMessage {
    fn from(s: &str) -> MQMessage {
        MQMessage::builder()
            .with_data(s.as_bytes())
            .build()
    }
}

impl From<String> for MQMessage {
    fn from(s: String) -> MQMessage {
        MQMessage::builder()
            .with_data(s.as_bytes())
            .build()
    } 
}

impl MQMessage {
    pub fn as_byte_vec(&self) -> Vec<u8> {
        let mut data = Vec::new();
        data.extend_from_slice(&self.get_headers_as_bytes());
        data.extend_from_slice(&self.data);
        data
    }
    pub fn as_string(&self) -> Result<String> {
        Ok(std::str::from_utf8(&self.data.as_ref())?.to_owned())
    }
    pub fn as_str(&self) -> Result<&str> {
        Ok(std::str::from_utf8(&self.data.as_ref())?)
    }
    pub fn get_descriptor(&self) -> &MQMD {
        &self.mqmd
    }
    pub fn add_header(&mut self, header: MQHeader) {
        self.headers.push(header);
    }
    pub fn get_headers(&self) -> &Vec<MQHeader> {
        &self.headers
    }
    fn get_headers_as_bytes(&self) -> Vec<u8> {
        let mut result = Vec::new();
        for header in &self.headers {
            result.extend_from_slice(&header.into_byte_vec());
        }
        result
    }
    pub fn builder() -> MQMessageBuilder {
        MQMessageBuilder::new()
    }
}

#[derive(Debug)]
pub struct MQMessageBuilder {
    mqmd: MQMD,
    data: Vec<u8>,
    headers: Vec<MQHeader>,
}

impl MQMessageBuilder { 
    pub fn new() -> MQMessageBuilder {
        MQMessageBuilder {
            mqmd: MQMD::builder().build(),
            data: Vec::new(),
            headers: Vec::new(),
        }
    }
    pub fn with_mqmd(&mut self, mqmd: MQMD) -> &mut Self {
        self.mqmd = mqmd;
        self
    }
    pub fn with_header(&mut self, header: MQHeader) -> &mut Self {
        self.headers.push(header);
        self
    }
    pub fn with_data(&mut self, data: &[u8]) -> &mut Self {
        self.data = Vec::from(data);
        self
    }
    pub fn build(&self) -> MQMessage {
        MQMessage {
            mqmd: self.mqmd.clone(),
            data: self.data.clone(),
            headers: self.headers.clone(), 
        }
    }
}

#[derive(Debug, Clone)]
pub struct MQMD {
    mqmd: mqi::MQMD,
}

impl MQMD {
    pub fn builder() -> MQMDBuilder {
        MQMDBuilder::new()
    }
    pub fn as_mut_ptr(&mut self) -> *mut mqi::MQMD {
        &mut self.mqmd as *mut _
    }
    pub fn as_ptr(&mut self) -> *const mqi::MQMD {
        &self.mqmd as *const _
    }
    pub fn as_mut_ref(&mut self) -> &mut mqi::MQMD {
        &mut self.mqmd
    }
}

#[derive(Clone)]
pub struct MQMDBuilder {
    mqmd: mqi::MQMD,
}

impl MQMDBuilder {
    pub fn new() -> MQMDBuilder {
        let mut mqmd: mqi::MQMD = unsafe { std::mem::zeroed() };
        unsafe {
            set_mqmd_default(&mut mqmd)
        };
        MQMDBuilder {
            mqmd
        }
    }
    pub fn build(&self) -> MQMD {
        MQMD { mqmd: self.mqmd }
    }
    pub fn version(&mut self, mqmd_ver: i32) -> &mut Self {
        self.mqmd.Version = mqmd_ver;
        self
    }
    pub fn expiry(&mut self, mqei: i32) -> &mut Self {
        self.mqmd.Expiry = mqei;
        self
    }
    pub fn msg_type(&mut self, mqmt: i32) -> &mut Self {
        self.mqmd.MsgType = mqmt;
        self
    }
    pub fn priority(&mut self, pri: i32) -> &mut Self {
        self.mqmd.Priority = pri;
        self
    }
    pub fn persistence(&mut self, mqper: i32) -> &mut Self {
        self.mqmd.Persistence = mqper;
        self
    }
    pub fn encoding(&mut self, mqenc: i32) -> &mut Self {
        self.mqmd.Encoding = mqenc;
        self
    }
    pub fn coded_charset_id(&mut self, mqccsi: i32) -> &mut Self {
        self.mqmd.CodedCharSetId = mqccsi;
        self
    }
    pub fn report(&mut self, mqro: i32) -> &mut Self {
        self.mqmd.Report = mqro;
        self
    }
    pub fn feedback(&mut self, mqfb: i32) -> &mut Self {
        self.mqmd.Feedback = mqfb;
        self
    }
    pub fn format(&mut self, mqfmt: &[u8; 9]) -> &mut Self {
        let mut format: [i8; 8] = [0;8];
        strcpy(mqfmt, &mut format, 8);
        self.mqmd.Format = format;
        self
    }
    pub fn reply_to_queue(&mut self, name: &str) -> &mut Self {
        let mut result: [i8;48] = [0;48];
        strcpy(name.as_bytes(), &mut result, name.len());
        self.mqmd.ReplyToQ = result;
        self
    }
}

impl Default for MQMDBuilder {
    fn default() -> MQMDBuilder {
        MQMDBuilder::new()
            .version(mqi::MQMD_VERSION_1) 
            .report(mqi::MQRO_NONE)
            .msg_type(mqi::MQMT_DATAGRAM)
            .expiry(mqi::MQEI_UNLIMITED)
            .feedback(mqi::MQFB_NONE)
            .priority(1)
            .encoding(mqi::MQENC_NATIVE)
            .coded_charset_id(mqi::MQCCSI_Q_MGR)
            .persistence(mqi::MQPER_PERSISTENT)
            .format(mqi::MQFMT_STRING)
            .reply_to_queue("").clone()
    }
}
